#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Purpose: Text export for HydroPlatform
# Created: 08/26/2009
# $Id $
# Copyright (c) 2008,2009 by University College London
# Authors:
#  Hossam Almeer <halmeer@ucl.cs.ac.uk>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with HydroPlatform for details.

import codecs
from itertools import count, chain
import logging
import numpy
import os
import sets
import datetime

from sqlalchemy import and_

from datetime import *
from hydroplatform.model.export import Exporter, ExportError, ExporterRegistry
from hydroplatform.model import network as nt
from hydroplatform.model import fields as fd
from hydroplatform.model import units

'''
This class exports the entire network into a single XML file. 
The intention of this is the easy visualisation of data if needed.  
Allow the user to export their data, and edit it with an XML GUI editor (Yigal Irani 
has give us permission to package and distribute his 'XML Marker'), or MS Excel
before importing it back into the project with all the modified data values. 
'''

DAILY        = 1 
WEEKLY       = 2 
BIWEEKLY     = 3 
MONTHLY      = 4 
SEASONNALITY = 6 
YEARLY       = 7 


class XMLExport(Exporter):
    '''
    Exports a network to several XML files. NOTE: This exporter is VERY 
    procedural.  It is therefore simple to understand, but makes a few 
    things harder. The reason for this is that it took a lot of trial and 
    error to get the format right for Excel to read the XML, which made for 
    very grundgy code, so messing with the format in any way will make it 
    useless. Furthermore, the structure of the output is depended on by the
    xml_import class.  What I'm really to say is DON'T MESS WITH THE FORMAT.
    TODO: Refactor this sucker!
    '''
    _name = "XML exporter"
    _description  = '''Exports a network to several XML files '''
  
    
    def __init__(self):
        Exporter.__init__(self)
        self.session = None    
        
    def export(self, network, fname, session=None):
        '''
        Export the given network to a set of XML files that are
        conveniently readable in Microsoft Excel. Would also work with
        any XML viewer, and probably other spreadsheet programs.
        '''
 
        # Used for building up timeseries
        season_delta = timedelta(weeks=13)
        year_delta = timedelta(weeks=52)
        day_delta = timedelta(hours=24)
        mnth_delta = timedelta(weeks=52/12.0)
        week_delta = timedelta(weeks=1)


        def add_timestep(date,tstep):
            '''
            Takes a datetime data and adds one of the respective timestep, 
            dependant on the timestep of the series, which must be passed in
            as a parameter of type int
            '''
            if tstep==SEASONNALITY:
                date = date + season_delta
            elif tstep==WEEKLY:
                date = date + week_delta
            elif tstep==DAILY:
                date = date + day_delta
            elif tstep==MONTHLY:
                date = date + mnth_delta
            elif tstep == YEARLY:
                date = date + year_delta
            else:
                date = date
            return date


        def get_field_type(field):
            '''
            Returns a string with the type of field
            '''
  
            if isinstance(field.attribute_type, fd.Parameter):
                return "Parameter"
            elif isinstance(field.attribute_type, fd.TimeSerie): #NOT fd.TimeSeries
                return "Time_Series"
            elif isinstance(field.attribute_type, fd.NodeReferences):
                return "Node_References"
            elif isinstance(field.attribute_type, fd.LinkReferences):
                return "Link_References"
            elif isinstance(field.attribute_type, fd.SeasonalParameter):
                return "Sesonal_Parameter"
            elif isinstance(field.attribute_type, fd.Table):
                return "Table"

 
        def update_link_names(network):
            '''
            updates the names of all the links in the network in case 
            they've been modified by the user
            '''
            for l in network.links:
                l.name = l.start.name+'_'+l.end.name #type of start/end = nt.Node
                session.flush()
            session.commit()

        def xmlify(tag):
            '''
            Modifies a tag so it is acceptable as an XML. Needed since HP 
            allows more flexibilty in object names than XML does in tag names
            '''
            if ('+',' ') in tag:
                tag = tag.replace(' ','-')
                tag = tag.replace('+','_OPPLUS_')
            if (tag[0].isdigit()):
                tag = '_' + tag
            return tag


        def export_obj_params(network, objects , fn): #fn is filename
            '''
            Writes Node or Link parameters into a file
            '''

            isnode = isinstance(objects[0],nt.Node) 
            row_tag, fn =  ('Node', fn + '_npar.xml') if (isnode) else \
                           ('Link', fn + '_lpar.xml')

            data_file = codecs.open(fn, "w", encoding="utf8")
            # TODO: xmlify on network name
            # net_name = network.name.replace(" ","-") # XML won't accept spaces in tags
            net_name = xmlify(network.name)
            data_file.write(" <%s> \n" % net_name)   # Top element

            for node in objects:
            # even though iter tag is node, here 'node' -> nt.Node || nt.Link
                data_file.write("  <%s>\n" %row_tag)
                data_file.write("    <id> %s </id>\n" %node.id) 
                data_file.write("    <name> %s </name>\n" %node.name) 
                data_file.write("    <type> %s </type>\n" %node.type.name) 
                for field in node.type.fields:
                    ftype = get_field_type(field)
                    fval = node.getFieldValue(session,field)
                    if 'Param' not in ftype: 
                        continue
                    else:
                        fval = fval.value if fval else 'None'
                        data_file.write("    <%s> %s </%s>" % \
                                        (field.name,fval,field.name))
                data_file.write("  </%s>\n" %row_tag)

            data_file.write(" </%s> " % net_name )
            data_file.close()


        fname = fname[:-4] if ('xml' in fname) else fname

        if session is None:
          raise ExportError(u"Cannot export without a session object")
        self.session = session

        update_link_names(network)
        export_obj_params(network,network.nodes,fname)
        export_obj_params(network,network.links,fname)

        # This is where we export time series, currently just for nodes, but
        # should be almost identical for links
        fname = fname [:-10]
        fname = fname+'_ts'
        data_file = codecs.open(fname, "w", encoding="utf8")

        all_vals = []
        for nd in network.nodes:
            for fld in nd.type.fields:
                if get_field_type(fld)=='Time_Series':
                    item_val = nd.getFieldValue(session,fld)
                    item_name = nd.name+'--'+fld.name
                    items = [item_name,item_val]
                    all_vals.append(items)

        '''
        The values array above is needed as the values in the TS are all
        sorted by date. It's just an idiosyncracy of Excel, if we do it this
        way then no schema is needed
        '''
        project = network.project
        tf = project.horizon_from
        tt = project.horizon_to
        tstep = project.time_step

        data_file.write(" <%s> \n" % net_name) # Top element
        dat = tf

        counter = 0
        while (dat<tt):
          data_file.write(" <Row>\n")
          data_file.write("  <Date>  %s </Date>\n" % dat)
          for i in range(len(all_vals)):
              tag = all_vals[i][0] 
              import pdb;pdb.set_trace()
              tag = tag.replace(' ','-') # Conforming to XML naming standards
              tag = tag.replace('+','-') 
              tag = tag.replace('3','THREE_')

              # print all_vals[i]
              if (all_vals[i][1]==None) or (all_vals[i][1].value == None):
                  val = 0
              else:
                  val = all_vals[i][1].value[counter][0]
              data_file.write("   <%s>  %s  </%s> \n" % ( tag,val,tag))
          data_file.write(" </Row>\n")
          counter += 1                   # moving to the next timeframe
          dat = add_timestep(dat,tstep)  # dat is for terminating condition
        
        data_file.write(" </%s> \n" % net_name)
           
REGISTRY = ExporterRegistry()
REGISTRY.register(XMLExport)
logging.info("XMLExport extension registered")        
